﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using Dominion.Core;
using Dominion.Core.Factories;
using Dominion.Core.Runners;
using log4net;


namespace Dominion.Core
{
    public class Player : INotifyPropertyChanged
    {

        private static readonly ILog log = LogManager.GetLogger(typeof(Player));

        public Player(string n, Game g, Strategy runner)
        {
            name = n;
            cards = SupplyFactory.Default;
            game = g;

            runner.Player = this;
            Runner = runner;


            MapEvents();
        }

        private void MapEvents()
        {

            GameEvents.MiliciaEvent += new EventHandler(this.Runner.MiliciaReaction);
        }


        #region Properties
        private Game game;
        public Game Game
        {
            get { return game; }
            set
            {
                game = value;
                OnPropertyChanged("Game");
            }
        }

        private String name;
        public String Name
        {
            get { return name; }
            set
            {
                name = value;
                OnPropertyChanged("Name");
            }
        }

        private Supply cards;
        public Supply Cards
        {
            get { return cards; }
            set
            {
                cards = value;
                OnPropertyChanged("Cards");
            }
        }

        private int buyCount = 0;
        public int BuyCount
        {
            get { return buyCount; }
            set
            {
                buyCount = value;
                OnPropertyChanged("BuyCount");
            }
        }

        private int actionCount = 0;
        public int ActionCount
        {
            get { return actionCount; }
            set
            {
                actionCount = value;
                OnPropertyChanged("ActionCount");
            }
        }


        private int buyBonus = 0;
        public int BuyBonus
        {
            get { return buyBonus; }
            set
            {
                buyBonus = value;
                OnPropertyChanged("BuyBonus");
            }
        }


        public int Coins
        {
            get { return Cards.Hand.OfType<ITreasure>().Sum(c => c.Value) - Cards.Buyed.OfType<ICard>().Sum(c => c.Price) + BuyBonus; }
        }

      
        public Strategy Runner { get; private set; }

        public CardsToAction CardsToAction { get; set; }

        public IEnumerable<Player> Others
        {
            get
            {
                return this.game.Players.Where(p => p != this);
            }
        }

        #endregion

        #region Methods

        public void BeginTurn()
        {

            BuyBonus = 0;
            ActionCount = 1;
            BuyCount = 1;
            CardsToAction = new CardsToAction();

            OnPropertyChanged("Coins");

            if (this.Runner.IsAuto)
            {
                Runner.Run(this);
            }
        }

        public void EndTurn()
        {
            Cards.DiscardHand();
            Cards.DiscardPlayedCards();
            Cards.DiscardBuyedCards();

            BuyBonus = 0;
            BuyCount = 0;
            ActionCount = 0;
            OnPropertyChanged("Coins");

            if (!game.IsTerminated)
            {
                Cards.DrawHand();
                Game.NextPlayer(this);
            }
        }


        public void Play(Kingdom card)
        {
            Cards.Hand.Remove(card);
            Cards.Played.Add(card);
            card.Behavior.Run(this);
            ActionCount--;
            OnPropertyChanged("Coins");
        }

        public bool CanBuyCard(string cardName)
        {
            bool result = BuyCount > 0;
            if (result)
            {
                var cards = Game.Board.Where((c) => c.Name.Equals(cardName));
                result = cards.Any();
                if (result)
                {
                    Card cardToBuy = cards.First();
                    int moneyInHand = Cards.Hand.OfType<ITreasure>().Sum(x => (int)(x.Value));
                    int moneySpend = Cards.Buyed.OfType<ICard>().Sum(x => (int)(x.Price));
                    result = cardToBuy.Price <= (moneyInHand - moneySpend + BuyBonus);
                }
            }
            return result;
        }


        public void BuyCard(string cardName)
        {
            var cards = Game.Board.Where((c) => c.Name.Equals(cardName));
            Card cardToBuy = cards.First();
            this.Game.Board.Remove(cardToBuy);
            Cards.Buyed.Add(cardToBuy);
            BuyCount--;
            this.Game.Messages.Enqueue(this.name + " buy " + cardName);
            OnPropertyChanged("Coins");

        }

        public void GainCard(Kind cardKind)
        {
            var cards = Game.Board.Where((c) => c.Kind.Equals(cardKind));
            if (cards.Any())
            {
                Card cardToGain = cards.First();
                this.Game.Board.Remove(cardToGain);
                Cards.Discard.Add(cardToGain);
                this.Game.Messages.Enqueue(this.name + " gain " + cardKind);
                OnPropertyChanged("Coins");
            }
            else
            {
                this.Game.Messages.Enqueue(this.name + " can't gain " + cardKind);
            }
        }

        public void Trash(Card card)
        {
            game.Trashed.Add(card);
            OnPropertyChanged("Coins");
        }

        #endregion

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(string propertyName)
        {
            var handler = PropertyChanged;
            if (handler != null)
            {
                var e = new PropertyChangedEventArgs(propertyName);
                handler(this, e);
            }
        }
        #endregion

        public override string ToString()
        {
            StringBuilder builder = new StringBuilder();
            builder.Append(this.name).AppendLine(" : ");
            builder.Append("Hand : ");
            this.Cards.Hand.ToList().ForEach(x => builder.Append(x.Name).Append(","));
            return builder.ToString();

        }
    }
}
